import base64
import os
import urlparse

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template

from pylib import environment
from pylib import models

# TODO(joe): Whitelist IPs for debugging?
_DEBUG = os.environ.get('SERVER_SOFTWARE', '').startswith('Dev')


class BaseHandler(webapp.RequestHandler):
  """Provides common app-specific utilities to our request handler.

  When you call render_response(), we augment the `template_args` with
  user specific information.
  """

  canonical_url = None

  def initialize(self, *args, **kwargs):
    webapp.RequestHandler.initialize(self, *args, **kwargs)
    self.url = self.request.url

  def render_response(self, template_name, **context):
    url = urlparse.urlparse(self.request.url)
    context.update({
      'login_url': users.create_login_url(self.url),
      'logout_url': users.create_logout_url(self.url),
      'xsrf_token': environment.XSRFToken(self.request, self.response),
      'on_dev': _DEBUG,
      'hostname': url.hostname,
    })

    user = users.get_current_user()
    if user:
      context['user'] = user.email()
      # No login interstitial page
      context['submit_method'] = 'post'
    else:
      # Login interstitial page
      context['submit_method'] = 'get'

    proto = self.request.get('proto')
    if proto:
      context['query_proto'] = proto

    path = os.path.join('..', 'templates', template_name)

    self.response.out.write(template.render(path, context, debug=_DEBUG))

  def internal_redirect(self, request_handler, key=None):
    """Redirect internally to a webapp.RequestHandler."""
    try:
      del self.request.str_GET['proto']
    except KeyError:
      pass

    try:
      del self.request.str_POST['proto']
    except KeyError:
      pass

    if key:
      self.request.str_GET['key'] = key
    request_handler.initialize(self.request, self.response)
    request_handler.get()

  def str_or_none(self, name):
    value = self.request.get(name)
    if value and value != 'Unknown':
      return value

  def int_or_none(self, name):
    value = self.str_or_none(name)
    if value:
      return int(value)

  def get(self):
    """Default aliasing to post for users coming through login."""
    self.post()


def valid_xsrf_token_required(original_handler):
  """Require a valid XSRF token in the environment, or error."""
  def Handler(self, *args, **kwargs):
    if self.request.get('xsrf_token') in environment.XSRFTokens(self.request,
                                                                self.response):
      original_handler(self, *args, **kwargs)
    else:
      self.error(403)
      self.response.out.write('Invalid token.')

  Handler.__name__ = original_handler.__name__
  return Handler


def model_from_request(original_handler):
  """Attempt to get a model using the query string data."""
  def Handler(self, *args, **kwargs):
    key = self.request.get('key')
    if key:
      if self.canonical_url:
        self.url = '%s?key=%s' % (self.canonical_url, key)
      model = models.db.get(key)
      return original_handler(self, model, *args, **kwargs)
    proto = self.request.get('proto')
    if proto:
      if self.canonical_url:
        self.url = '%s?proto=%s' % (self.canonical_url, proto)
      model = models.db.model_from_protobuf(base64.b64decode(proto))
      return original_handler(self, model, *args, **kwargs)
    self.error(500)
    self.response.out.write('Missing query string parameters')

  Handler.__name__ = original_handler.__name__
  return Handler


def transaction_with_retries(original_handler):
  """Run in a transaction with retries."""
  def Handler(self, *args, **kwargs):
    return models.db.run_in_transaction_custom_retries(
        3, original_handler, self, *args, **kwargs)

  Handler.__name__ = original_handler.__name__
  return Handler


def retry(original_handler):
  """Retry page generation for datastore failures."""
  def Handler(self, *args, **kwargs):
    for _ in xrange(3):
      try:
        return original_handler(self, *args, **kwargs)
      except models.db.Timeout:
        pass
    return original_handler(self, *args, **kwargs)

  Handler.__name__ = original_handler.__name__
  return Handler
